import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';

import '../ftp_entry.dart';
import '../ftp_exceptions.dart';
import '../ftp_reply.dart';
import '../ftp_socket.dart';
import '../ftpconnect_base.dart';
import '../utils.dart';

class FTPDirectory {
  final FTPSocket _socket;

  FTPDirectory(this._socket);

  Future<bool> makeDirectory(String sName) async {
    FTPReply sResponse = await (_socket.sendCommand('MKD $sName'));

    return sResponse.isSuccessCode();
  }

  Future<bool> deleteEmptyDirectory(String? sName) async {
    FTPReply sResponse = await (_socket.sendCommand('rmd $sName'));

    return sResponse.isSuccessCode();
  }

  Future<bool> changeDirectory(String? sName) async {
    FTPReply sResponse = await (_socket.sendCommand('CWD $sName'));

    return sResponse.isSuccessCode();
  }

  Future<String> currentDirectory() async {
    FTPReply sResponse = await _socket.sendCommand('PWD');
    if (!sResponse.isSuccessCode()) {
      throw FTPConnectException(
          'Failed to get current working directory', sResponse.message);
    }

    int iStart = sResponse.message.indexOf('"') + 1;
    int iEnd = sResponse.message.lastIndexOf('"');

    return sResponse.message.substring(iStart, iEnd);
  }

  Future<List<FTPEntry>> directoryContent({String? path}) async {
    // Enter passive mode
    FTPReply response = await _socket.openDataTransferChannel();

    String listCmd = path != null ? "LIST $path" : "LIST";
    _socket.sendCommandWithoutWaitingResponse(listCmd);

    // Data transfer socket
    int iPort = Utils.parsePort(response.message, _socket.supportIPV6);
    SecurityContext context = SecurityContext.defaultContext;
    RawSecureSocket dataSocket = await RawSecureSocket.connect(
      _socket.host,
      iPort,
      timeout: Duration(seconds: _socket.timeout),
      context: context,
      onBadCertificate: (certificate) => true,
    );

    response = await _socket.readResponse();
    bool isTransferCompleted = response.isSuccessCode();
    if (!isTransferCompleted && response.code != 125 && response.code != 150) {
      throw FTPConnectException('Connection refused. ', response.message);
    }

    final completer = Completer<List<FTPEntry>>();
    List<FTPEntry> lstFTPEntries = <FTPEntry>[];
    List<int> lstDirectoryListing = [];
    bool isStreamDone = false; // 标记流是否已处理完成
    String dataString ="";
    // 获取订阅对象
    final subscription = dataSocket.listen((RawSocketEvent event) {
      print("event: $event");
      if (event == RawSocketEvent.read) {
        var data = dataSocket.read();
        if (data != null) {
          lstDirectoryListing.addAll(data);
          String subDataString = utf8.decode(data);
          dataString = dataString + subDataString;
          print(dataString);
        }
      } else if (event == RawSocketEvent.closed) {
        isStreamDone = true; // 流自然关闭时标记
        dataSocket.close();
      }
    }, onError: (error) {
      isStreamDone = true; // 出错时也标记
      if (!completer.isCompleted) completer.completeError(error);
    });

    // 关键：等待asFuture()，但设置超时强制唤醒，避免无限阻塞
    await Future.any([
      subscription.asFuture(), // 正常等待流关闭
      Future.delayed(Duration(seconds: 10), () {
        // 超时未关闭，手动标记完成
        isStreamDone = true;
        print("Stream timeout, forcing completion");
      }),
    ]);

    dataSocket.close();
    await _socket.readResponse();

    // 无论流是自然关闭还是超时，都完成completer
    if (!completer.isCompleted) {
      completer.complete(lstFTPEntries);
    }

    // 此时一定能执行到这里
    final entries = await completer.future;

    // 解析逻辑保留
    dataString.split('\n').forEach((line) {
      if (line.trim().isNotEmpty) {
        lstFTPEntries.add(
          FTPEntry.parse(line.replaceAll('\r', ""), _socket.listCommand),
        );
      }
    });
    return entries;
  }

  Future<List<FTPEntry>> directoryContent1({String? path}) async {
    // Enter passive mode
    FTPReply response = await _socket.openDataTransferChannel();

    String listCmd = path != null ? "LIST $path" : "LIST";
    // Directoy content listing, the response will be handled by another socket
    _socket.sendCommandWithoutWaitingResponse(listCmd);

    // Data transfer socket
    int iPort = Utils.parsePort(response.message, _socket.supportIPV6);
    SecurityContext context = SecurityContext.defaultContext;
    RawSecureSocket dataSocket = await RawSecureSocket.connect(
      _socket.host,
      iPort,
      timeout: Duration(seconds: _socket.timeout),
      context: context,
      onBadCertificate: (certificate) => true,
    );
    //Test if second socket connection accepted or not
    response = await _socket.readResponse();
    //some server return two lines 125 and 226 for transfer finished
    bool isTransferCompleted = response.isSuccessCode();
    if (!isTransferCompleted && response.code != 125 && response.code != 150) {
      throw FTPConnectException('Connection refused. ', response.message);
    }

    List<int> lstDirectoryListing = [];
    await dataSocket.listen((RawSocketEvent event) {
      if (event == RawSocketEvent.read) {
        var data = dataSocket.read();
        if (data != null) {
          lstDirectoryListing.addAll(data);
        }
      } else if (event == RawSocketEvent.closed) {

      }
    }).asFuture();

    if (!isTransferCompleted) {
      response = await _socket.readResponse();
    }
    await dataSocket.close();

    // Convert MLSD response into FTPEntry
    List<FTPEntry> lstFTPEntries = <FTPEntry>[];
    Utf8Codec().decode(lstDirectoryListing).split('\n').forEach((line) {
      if (line.trim().isNotEmpty) {
        lstFTPEntries.add(
          FTPEntry.parse(line.replaceAll('\r', ""), _socket.listCommand),
        );
      }
    });

    return lstFTPEntries;
  }


  Future<List<String>> directoryContentNames() async {
    var list = await directoryContent();
    return list.map((f) => f.name).whereType<String>().toList();
  }
}
